﻿/*
 * This file is part of MonoSettlers.
 *
 * Copyright (C) 2010-2011 Christoph Husse
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: 
 *      # Christoph Husse
 * 
 * Also checkout our homepage: http://opensettlers.codeplex.com/
 */
using System;
using System.ComponentModel;
using System.Runtime.Serialization;
using System.IO;

namespace MonoSettlers
{
    /// <summary>
    /// An animation set is able to play several animations simultaneously.
    /// </summary>
    public class AnimationSet
    {
        private UniqueMap<String, Animation> m_Animations = new UniqueMap<string, Animation>();
        private Int64 m_DurationMillis;

        public AnimationClass Class { get; private set; }
        public String Name { get; internal set; }
        public Int32 Index { get; internal set; }
        public Int64 DurationMillis { get { return m_DurationMillis; } set { Library.Modify(); m_DurationMillis = value; } }

        internal void Save(BinaryWriter inWriter)
        {
            // write set to stream
            inWriter.Write((Byte)3); // set type ID
            inWriter.Write((UInt16)0x1000); // set version

            inWriter.Write((String)Name);
            inWriter.Write((Int64)DurationMillis);
            inWriter.Write((Int32)m_Animations.Count);

            foreach (var anim in m_Animations.Values)
            {
                anim.Save(inWriter);
            }
        }

        internal static AnimationSet Load(AnimationClass inClass, BinaryReader inReader)
        {
            AnimationSet result;

            if (inReader.ReadByte() != 3)
                throw new InvalidDataException();

            switch (inReader.ReadUInt16())
            {
                case 0x1000:
                    {
                        result = new AnimationSet(inReader.ReadString(), inClass);
                        result.DurationMillis = inReader.ReadInt64();

                        for (int i = 0, count = inReader.ReadInt32(); i < count; i++)
                        {
                            result.m_Animations.Add(result.Name, Animation.Load(inClass.Library, result, inReader));
                        }
                    } break;

                default:
                    throw new InvalidDataException();
            }

            foreach (var anim in result.m_Animations.Values)
            {
                anim.OnDimensionChanged += result.ComputeDimension;
            }

            return result;
        }

        public BindingList<Animation> Animations { get { return m_Animations.GetValueBinding(); } }
        public BindingList<Animation> Children { get { return Animations; } }
        public AnimationLibrary Library { get { return Class.Library; } }
        public Int32 Width { get; private set; }
        public Int32 Height { get; private set; }

        internal AnimationSet(String inName, AnimationClass inClass)
        {
            Name = inName;
            Class = inClass;
        }

        internal void ComputeDimension()
        {
            ComputeDimension(true);
        }

        internal void ComputeDimension(Boolean inNotifyParent)
        {
            Int32 newWidth = 0;
            Int32 newHeight = 0;

            foreach (var anim in m_Animations.Values)
            {
                newWidth = Math.Max(newWidth, anim.OffsetX + anim.Width);
                newHeight = Math.Max(newHeight, anim.OffsetY + anim.Height);
            }

            Width = newWidth;
            Height = newHeight;

            if(inNotifyParent)
                Class.ComputeDimension();
        }

        public void Rename(Animation inAnimation, String inNewName)
        {
            Library.Modify();

            Library.ValidateName(inNewName);

            if (m_Animations.ContainsKey(inNewName))
                throw new ArgumentException("An animation named \"" + inNewName + "\" does already exist!");

            int pos;

            if ((pos = m_Animations.Values.IndexOf(inAnimation)) < 0)
                throw new ApplicationException("Animation does not belong to this set.");

            m_Animations.Remove(inAnimation.Name);
            inAnimation.Name = inNewName;
            m_Animations.Add(inAnimation.Name, inAnimation);
        }

        public Animation AddAnimation(String inName)
        {
            Library.Modify();

            Animation result = new Animation(inName, this);

            Library.ValidateName(inName);

            if (m_Animations.ContainsKey(inName))
                throw new ArgumentException("An animation set named \"" + inName + "\" does already exist!");

            m_Animations.Add(inName, result);

            result.OnDimensionChanged += ComputeDimension;
            ComputeDimension();

            return result;
        }

        public void RemoveAnimation(Animation inAnimation)
        {
            Library.Modify();

            if (m_Animations.Remove(inAnimation.Name))
            {
                inAnimation.OnDimensionChanged -= ComputeDimension;

                ComputeDimension();
            }
        }

    }
}
